package chagine.core.util;

import chagine.core.ErrorCode;
import chagine.core.exception.SeeingException;
import chagine.core.exception.SeeingRuntimeException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;

import javax.validation.ConstraintViolationException;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * com.seeing.core.rpc.util.ExceptionUtils
 * Created by 王彬安（wba）on 2018/3/24.
 */
public class ExceptionUtils {
    private ExceptionUtils() {
    }

    public static SeeingRuntimeException getSeeingRuntimeException(Throwable cause) {
        if (cause == null) {
            return null;
        }
        if (cause instanceof SeeingRuntimeException) {
            return (SeeingRuntimeException) cause;
        }

        while (cause.getCause() != null && cause.getCause() != cause) {
            cause = cause.getCause();
            if (cause instanceof SeeingRuntimeException) {
                return (SeeingRuntimeException) cause;
            }
        }
        return null;
    }

    public static SeeingException getSeeingException(Throwable cause) {
        if (cause == null) {
            return null;
        }
        if (cause instanceof SeeingException) {
            return (SeeingException) cause;
        }
        if (cause instanceof SeeingRuntimeException) {
            return ((SeeingRuntimeException) cause).getSeeingException();
        }

        while (cause.getCause() != null && cause.getCause() != cause) {
            cause = cause.getCause();
            if (cause instanceof SeeingException) {
                return (SeeingException) cause;
            }
            if (cause instanceof SeeingRuntimeException) {
                return ((SeeingRuntimeException) cause).getSeeingException();
            }
        }
        return null;
    }

    public static String getSeeingCode(Throwable cause) {
        if (cause == null) {
            return ErrorCode.UNKNOWN_EXCEPTION;
        }
        if (cause instanceof SeeingException) {
            return ((SeeingException) cause).getCode();
        }
        if (cause instanceof SeeingRuntimeException) {
            return ((SeeingRuntimeException) cause).getCode();
        }
        if (cause instanceof ConstraintViolationException) {
            return ErrorCode.ILLEGALARGUMENT_EXCEPTION;
        }
        if (cause instanceof MethodArgumentNotValidException) {
            return ErrorCode.ILLEGALARGUMENT_EXCEPTION;
        }
        if (cause instanceof BindException) {
            return ErrorCode.ILLEGALARGUMENT_EXCEPTION;
        }
        while (cause.getCause() != null && cause.getCause() != cause) {
            cause = cause.getCause();
            if (cause instanceof SeeingException) {
                return ((SeeingException) cause).getCode();
            }
            if (cause instanceof SeeingRuntimeException) {
                return ((SeeingRuntimeException) cause).getCode();
            }
        }
        return ErrorCode.UNKNOWN_EXCEPTION;
    }

    public static String getCauseMessage(Throwable cause) {
        if (cause == null) {
            return "";
        }

        if (cause instanceof SeeingException) {
            return cause.getMessage();
        }
        if (cause instanceof SeeingRuntimeException) {
            return cause.getMessage();
        }

        StringBuilder builder = new StringBuilder();
        builder.append(getMessage(cause));
        while (cause.getCause() != null && cause.getCause() != cause) {
            builder.append(",");
            cause = cause.getCause();

            if (cause instanceof SeeingException) {
                return cause.getMessage();
            }
            if (cause instanceof SeeingRuntimeException) {
                return cause.getMessage();
            }

            builder.append(getMessage(cause));
        }
        return builder.toString();
    }

    private static String getMessage(Throwable cause) {
        String message = cause.getMessage();
        if (message != null) {
            return message;
        } else {
            return cause.getClass().toString();
        }
    }

    /**
     * Gets the stack trace of the provided throwable as a string.
     *
     * @param t the exception to get the stack trace for.
     * @return the stack trace as a string.
     */
    public static String exceptionStackTraceAsString(Throwable t) {
        StringWriter sw = new StringWriter();
        t.printStackTrace(new PrintWriter(sw));
        return sw.toString().replaceAll("\r", " ").replaceAll("\n", " ");
    }
}
